// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"runtime/internal/atomic"
	"runtime/internal/sys"
	"unsafe"
)

const (
	_WorkbufSize = 2048 // 以字节为单位；值越大，争用越少

	// workbufAlloc是一次为新的workbuf分配的字节数
	// 。这必须是pageSize的倍数，而
	// 应该是_WorkbufSize的倍数。
	// 
	// 较大的值可减少workbuf分配开销。较小的
	// 值会减少堆碎片。
	workbufAlloc = 32 << 10
)

func init() {
	if workbufAlloc%pageSize != 0 || workbufAlloc%_WorkbufSize != 0 {
		throw("bad workbufAlloc")
	}
}

// 垃圾收集器工作池抽象。
// 
// 这实现了一个生产者/消费者模型，用于指向灰色
// 对象的指针。灰色对象是在工作
// 队列上标记的对象。黑色对象已标记，不在工作队列上。
// 
// 写屏障、根发现、堆栈扫描和对象扫描
// 生成指向灰色对象的指针。扫描使用指向灰色对象的指针，从而使它们变黑，然后扫描它们，可能产生指向灰色对象的新指针。

// gcWork为
// 垃圾收集器提供生成和使用工作的接口。
// 
// gcw:=&getg（）.m.p.ptr（）.gcw 
// 。。调用gcw.put（）生成，调用gcw.tryGet（）消耗。。
// 
// 在标记阶段使用gcWork必须防止垃圾收集器转换为标记终止，因为
// gcWork可能在本地持有GC工作缓冲区。这可以通过
// 禁用抢占（systemstack或acquirem）来实现。
type gcWork struct {
	// wbuf1和wbuf2是主要和次要工作缓冲区。
	// 
	// 这可以看作是两个工作缓冲区的
	// 指针串联的堆栈。当我们弹出最后一个指针时，我们通过引入一个新的
	// 满缓冲区并丢弃一个空缓冲区，将堆栈向上移动一个工作缓冲区。当我们填充两个
	// 缓冲区时，我们通过
	// 将堆栈向下移动一个工作缓冲区，引入一个新的空缓冲区并丢弃一个完整的缓冲区。
	// 这样我们就有了一个缓冲区的迟滞值，即
	// 将获得或放置工作缓冲区的成本分摊到
	// 至少一个工作缓冲区上，并减少了
	// 全局工作列表上的争用。
	// 
	// wbuf1始终是我们当前推送到的缓冲区，而
	// 从中弹出，wbuf2是将被丢弃的缓冲区
	// 下一步。
	// 
	// 不变量：wbuf1和wbuf2均为零或均为零。
	wbuf1, wbuf2 *workbuf

	// 此gcWork上标记（变黑）的字节。这是将
	// 聚合到work.BYTES中，标记为dispose。
	bytesMarked uint64

	// 扫描在此gcWork上执行的工作。这通过dispose聚合到
	// gcController中，也可能被调用方刷新。
	scanWork int64

	// flushedWork表示自上次gcMarkDone 
	// 终止检查以来，非空工作缓冲区被刷新到全局工作列表。具体而言，这表明此
	// gcWork可能已将工作传达给另一个gcWork。
	flushedWork bool
}

// 大多数gcWork方法都是go:nowritebarrierrec，因为
// write barrier本身可以调用gcWork方法，但这些方法都是
// 通常不可重入的。因此，如果gcWork方法在gcWork处于不一致状态时调用了
// 写屏障，而
// 写屏障又调用了gcWork方法，则可能会永久损坏gcWork。

func (w *gcWork) init() {
	w.wbuf1 = getempty()
	wbuf2 := trygetfull()
	if wbuf2 == nil {
		wbuf2 = getempty()
	}
	w.wbuf2 = wbuf2
}

// put将一个指针排入队列，以便垃圾收集器进行跟踪。
// obj必须指向heap对象或oblet的开头。
// go:nowritebarrierrec 
func (w *gcWork) put(obj uintptr) {
	flushed := false
	wbuf := w.wbuf1
	// 记录这可能会获取wbufSpans或堆锁以分配一个workbuf。
	lockWithRankMayAcquire(&work.wbufSpans.lock, lockRankWbufSpans)
	lockWithRankMayAcquire(&mheap_.lock, lockRankMheap)
	if wbuf == nil {
		w.init()
		wbuf = w.wbuf1
		// wbuf此时为空。
	} else if wbuf.nobj == len(wbuf.obj) {
		w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
		wbuf = w.wbuf1
		if wbuf.nobj == len(wbuf.obj) {
			putfull(wbuf)
			w.flushedWork = true
			wbuf = getempty()
			w.wbuf1 = wbuf
			flushed = true
		}
	}

	wbuf.obj[wbuf.nobj] = obj
	wbuf.nobj++

	// 如果我们将缓冲区设置为满，请让GC控制器知道，这样可以鼓励更多工作人员运行。我们将此延迟到
	// put的末尾，以便w处于一致状态，因为
	// 工作人员可能自己操纵w。
	if flushed && gcphase == _GCmark {
		gcController.enlistWorker()
	}
}

// putFast执行put并报告是否可以快速执行
// 否则返回false，调用方需要调用put。
// go:nowritebarrierrec 
func (w *gcWork) putFast(obj uintptr) bool {
	wbuf := w.wbuf1
	if wbuf == nil {
		return false
	} else if wbuf.nobj == len(wbuf.obj) {
		return false
	}

	wbuf.obj[wbuf.nobj] = obj
	wbuf.nobj++
	return true
}

// putBatch对obj中的每个指针执行put操作。有关这些指针的约束，请参见put。
// 
// go:nowritebarrierrec 
func (w *gcWork) putBatch(obj []uintptr) {
	if len(obj) == 0 {
		return
	}

	flushed := false
	wbuf := w.wbuf1
	if wbuf == nil {
		w.init()
		wbuf = w.wbuf1
	}

	for len(obj) > 0 {
		for wbuf.nobj == len(wbuf.obj) {
			putfull(wbuf)
			w.flushedWork = true
			w.wbuf1, w.wbuf2 = w.wbuf2, getempty()
			wbuf = w.wbuf1
			flushed = true
		}
		n := copy(wbuf.obj[wbuf.nobj:], obj)
		wbuf.nobj += n
		obj = obj[n:]
	}

	if flushed && gcphase == _GCmark {
		gcController.enlistWorker()
	}
}

// tryGet将垃圾收集器跟踪的指针出列。
// 
// 如果此gcWork或全局
// 队列中没有剩余指针，tryGet返回0。请注意，
// 其他gcWork实例或其他缓存中可能仍有指针。
// go:nowritebarrierrec 
func (w *gcWork) tryGet() uintptr {
	wbuf := w.wbuf1
	if wbuf == nil {
		w.init()
		wbuf = w.wbuf1
		// wbuf此时为空。
	}
	if wbuf.nobj == 0 {
		w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
		wbuf = w.wbuf1
		if wbuf.nobj == 0 {
			owbuf := wbuf
			wbuf = trygetfull()
			if wbuf == nil {
				return 0
			}
			putempty(owbuf)
			w.wbuf1 = wbuf
		}
	}

	wbuf.nobj--
	return wbuf.obj[wbuf.nobj]
}

// tryGetFast将垃圾收集器的指针出列，以跟踪
// 如果一个指针随时可用。否则返回0和
// 调用者应该调用tryGet（）。
// go:nowritebarrierrec 
func (w *gcWork) tryGetFast() uintptr {
	wbuf := w.wbuf1
	if wbuf == nil {
		return 0
	}
	if wbuf.nobj == 0 {
		return 0
	}

	wbuf.nobj--
	return wbuf.obj[wbuf.nobj]
}

// dispose返回所有缓存到全局队列的指针。
// 缓冲区被放在完整队列上，这样在
// 写屏障不会简单地重新获取它们。这有助于减少mutator在并发标记阶段隐藏指针的
// GC可以检查它们之前，
// 能力。
// 
// go:nowritebarrierrec 
func (w *gcWork) dispose() {
	if wbuf := w.wbuf1; wbuf != nil {
		if wbuf.nobj == 0 {
			putempty(wbuf)
		} else {
			putfull(wbuf)
			w.flushedWork = true
		}
		w.wbuf1 = nil

		wbuf = w.wbuf2
		if wbuf.nobj == 0 {
			putempty(wbuf)
		} else {
			putfull(wbuf)
			w.flushedWork = true
		}
		w.wbuf2 = nil
	}
	if w.bytesMarked != 0 {
		// 处置发生的频率相对较低。如果此
		// 原子成为问题，我们应首先尝试
		// 减少处理量，必要时将其聚合到per-P 
		// 计数器中。
		atomic.Xadd64(&work.bytesMarked, int64(w.bytesMarked))
		w.bytesMarked = 0
	}
	if w.scanWork != 0 {
		atomic.Xaddint64(&gcController.scanWork, w.scanWork)
		w.scanWork = 0
	}
}

// balance将此gcWork中缓存的某些工作移回
// 全局队列。
// go:nowritebarrierrec 
func (w *gcWork) balance() {
	if w.wbuf1 == nil {
		return
	}
	if wbuf := w.wbuf2; wbuf.nobj != 0 {
		putfull(wbuf)
		w.flushedWork = true
		w.wbuf2 = getempty()
	} else if wbuf := w.wbuf1; wbuf.nobj > 4 {
		w.wbuf1 = handoff(wbuf)
		w.flushedWork = true // 切换未将全部
	} else {
		return
	}
	// 我们已将缓冲区刷新到完整列表，因此请唤醒一名工作人员。
	if gcphase == _GCmark {
		gcController.enlistWorker()
	}
}

// 空报告w是否没有可用的标记工作。
// go:nowritebarrierrec 
func (w *gcWork) empty() bool {
	return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0)
}

// 在内部，GC工作池保存在工作缓冲区中的数组中。
// gcWork接口将工作缓冲区缓存到工作缓冲区满（或空）为止，以避免在全局工作缓冲区列表上争用。

type workbufhdr struct {
	node lfnode // 必须是第一个
	nobj int
}

// go:notinheap 
type workbuf struct {
	workbufhdr
	// 说明上述字段
	obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / sys.PtrSize]uintptr
}

// 工厂例程的工作。这些函数用于管理
// workbufs。
// 如果GC要求一些工作，这些是唯一使wbufs对GC可用的例程。

func (b *workbuf) checknonempty() {
	if b.nobj == 0 {
		throw("workbuf is empty")
	}
}

func (b *workbuf) checkempty() {
	if b.nobj != 0 {
		throw("workbuf is not empty")
	}
}

// getempty从work.empty列表中弹出一个空的工作缓冲区，
// 如果没有可用的缓冲区，则分配新的缓冲区。
// go:nowritebarrier 
func getempty() *workbuf {
	var b *workbuf
	if work.empty != 0 {
		b = (*workbuf)(work.empty.pop())
		if b != nil {
			b.checkempty()
		}
	}
	// 记录这可能会获取wbufSpans或堆锁以分配一个工作BUF。
	lockWithRankMayAcquire(&work.wbufSpans.lock, lockRankWbufSpans)
	lockWithRankMayAcquire(&mheap_.lock, lockRankMheap)
	if b == nil {
		// 分配更多工作。
		var s *mspan
		if work.wbufSpans.free.first != nil {
			lock(&work.wbufSpans.lock)
			s = work.wbufSpans.free.first
			if s != nil {
				work.wbufSpans.free.remove(s)
				work.wbufSpans.busy.insert(s)
			}
			unlock(&work.wbufSpans.lock)
		}
		if s == nil {
			systemstack(func() {
				s = mheap_.allocManual(workbufAlloc/pageSize, spanAllocWorkBuf)
			})
			if s == nil {
				throw("out of memory")
			}
			// 在忙列表中记录新的跨度。
			lock(&work.wbufSpans.lock)
			work.wbufSpans.busy.insert(s)
			unlock(&work.wbufSpans.lock)
		}
		// 将跨度分割为新的工作区。返回一个，
		// 将其余的放在空列表中。
		for i := uintptr(0); i+_WorkbufSize <= workbufAlloc; i += _WorkbufSize {
			newb := (*workbuf)(unsafe.Pointer(s.base() + i))
			newb.nobj = 0
			lfnodeValidate(&newb.node)
			if i == 0 {
				b = newb
			} else {
				putempty(newb)
			}
		}
	}
	return b
}

// putempty将一个workbuf放入work.empty列表。
// 一经输入，此goroutine就拥有b。lfstack.push放弃所有权。
// go:nowritebarrier 
func putempty(b *workbuf) {
	b.checkempty()
	work.empty.push(&b.node)
}

// putfull将workbuf放在GC的work.full列表中。
// putfull接受部分满缓冲区，因此GC可以避免与部分满缓冲区所有权的变体竞争
// 。
// go:nowritebarrier 
func putfull(b *workbuf) {
	b.checknonempty()
	work.full.push(&b.node)
}

// trygetfull尝试获取已满或部分空的工作缓冲区。
// 如果不能立即使用，则返回nil 
// 转到：nowritebarrier 
func trygetfull() *workbuf {
	b := (*workbuf)(work.full.pop())
	if b != nil {
		b.checknonempty()
		return b
	}
	return b
}

// 转到：nowritebarrier 
func handoff(b *workbuf) *workbuf {
	// 使用b的一半指针创建新缓冲区。
	b1 := getempty()
	n := b.nobj / 2
	b.nobj -= n
	b1.nobj = n
	memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), uintptr(n)*unsafe.Sizeof(b1.obj[0]))

	// 将b放在完整列表中-让b的前半部分被偷。
	putfull(b)
	return b1
}

// prepareFreeWorkbufs将忙碌的工作buf范围移动到空闲列表，以便它们可以被释放到堆中。只有当所有
// WorkBuf都在空列表中时，才能调用此函数。
func prepareFreeWorkbufs() {
	lock(&work.wbufSpans.lock)
	if work.full != 0 {
		throw("cannot free workbufs when work.full != 0")
	}
	// 由于所有工作单元都在空列表中，所以我们不关心
	// 哪些工作单元在哪个跨度中。我们可以擦除整个空
	// 列表，并将所有workbuf跨距移到空闲列表中。
	work.empty = 0
	work.wbufSpans.free.takeAll(&work.wbufSpans.busy)
	unlock(&work.wbufSpans.lock)
}

// freeSomeWbufs将一些workbuf释放回堆，如果应该再次调用以释放更多，则返回
// true。
func freeSomeWbufs(preemptible bool) bool {
	const batchSize = 64 // /~1–2µs/量程。
	lock(&work.wbufSpans.lock)
	if gcphase != _GCoff || work.wbufSpans.free.isEmpty() {
		unlock(&work.wbufSpans.lock)
		return false
	}
	systemstack(func() {
		gp := getg().m.curg
		for i := 0; i < batchSize && !(preemptible && gp.preempt); i++ {
			span := work.wbufSpans.free.first
			if span == nil {
				break
			}
			work.wbufSpans.free.remove(span)
			mheap_.freeManual(span, spanAllocWorkBuf)
		}
	})
	more := !work.wbufSpans.free.isEmpty()
	unlock(&work.wbufSpans.lock)
	return more
}
